﻿using Exiled.API.Features;
using Exiled.API.Features.Pools;
using Exiled.Events.EventArgs.Player;
using MEC;
using RgCore.API;
using RgCore.GameData;
using RgCore.GameModes;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RgCore.PlayerHud
{
    public class PlainMessage : Message
    {
        public PlainMessage(string msgid, uint duration, ScreenLocation sl, params string[] content) : base(msgid, duration, sl)
        {
            Content = content;
        }

        public string[] Content { get; private set; }

        public override string[] GetMsg() => Content;

        public void UpdateContent(params string[] content)
        {
            Content = content;
        }
    }

    public class DynamicMessage : Message
    {
        public DynamicMessage(string msgid, Player instance, Func<Player, string[]> getter, uint duration, ScreenLocation sl) : base(msgid, duration, sl)
        {
            Getter = getter;
            Instance = instance;
        }

        public Player Instance { get; }
        public Func<Player, string[]> Getter { get; }

        public override string[] GetMsg() => Getter(Instance);
    }

    public abstract class Message
    {
        public Message(string msgid, uint duration, ScreenLocation sl)
        {
            MsgId = msgid;
            Duration = duration;
            Created = DateTime.Now;
            Location = sl;
        }

        public string MsgId { get; }

        public abstract string[] GetMsg();

        public DateTime Created { get; }
        public uint Duration { get; }
        public ScreenLocation Location { get; }
    }

    public class PlayerHud
    {
        public PlayerHud(Player player)
        {
            Player = player;
        }

        public Player Player { get; }

        public List<Message> _messages = new List<Message>();
        private DateTime _lastUpdate = DateTime.Now;
        private StringBuilder builder = new StringBuilder();

        public const int Top_Lines = 5; //1-5 行
        public const int Bottom_Lines = 7; //32-38行
        public const int CenterTop_Lines = 9; //6-14行
        public const int CenterBottom_Lines = 6; //26-31
        public static int Center_Lines => 38 - (Top_Lines + Bottom_Lines + CenterTop_Lines + CenterBottom_Lines);// 15-25行

        public static string[] ProfileLine(Player Player)
        {
            string text = "<size=50%><color=yellow>Loading</color></size>";
            GameplayData gd = GameplayData.GameDatas.FirstOrDefault(x => x.Player == Player);
            if (gd != null)
            {
                try
                {
                    if (Player.DoNotTrack)
                    {
                        text = $"<size=50%><color=#00FF00>{Player.Nickname}</color> | <color=#CD0000>[DO NOT TRACK 已启动]</color> <color=#FF7256>你的数据不会被记录</color> | TPS: {Server.Tps}</size>";
                    }
                    else
                    {
                        string exp = $"<color=#00FF00>{gd.Experience}</color> / <color=#00FF00>{gd.MaxExperience}</color>";
                        string detailed = $"| 积分: <color=#FFD700>{gd.Points}</color> ";
                        if (gd.DetailedMode || !Round.IsStarted)
                        {
                            detailed += $"| MVP: <color=#00FF00>{gd.Mvp}</color> ";
                        }
                        text = $"<size=50%><color=#00FF00>{Player.Nickname}</color> " +
                            $"{GameplayData.GetLevelText(gd.Transmigration, gd.Level, true)} | EXP: {exp} {detailed}| 分数: <color=#e1ff48>{(GameMode.CurrentGameMode.Attribute.ScoreEnabled ? gd.Score.ToString() : "禁用")}</color> | TPS: {Server.Tps}</size>";
                    }
                }
                catch (Exception e)
                {
                    Log.Error(e);
                }
            }
            return new string[] { text };
        }

        public void Show()
        {
            _lastUpdate = DateTime.Now;
            builder.Clear();
            builder.Append("");
            var messages = ListPool<Message>.Pool.Get(_messages);
            List<string> tops = ListPool<string>.Pool.Get();
            List<string> center_tops = ListPool<string>.Pool.Get();
            List<string> centers = ListPool<string>.Pool.Get();
            List<string> center_bottoms = ListPool<string>.Pool.Get();
            List<string> bottoms = ListPool<string>.Pool.Get();
            foreach (var message in messages)
            {
                switch (message.Location)
                {
                    case ScreenLocation.Top: tops.AddRange(message.GetMsg()); break;
                    case ScreenLocation.CenterTop: center_tops.AddRange(message.GetMsg()); break;
                    case ScreenLocation.Center: centers.AddRange(message.GetMsg()); break;
                    case ScreenLocation.CenterBottom: center_bottoms.AddRange(message.GetMsg());break;
                    case ScreenLocation.Bottom: bottoms.AddRange(message.GetMsg());break;
                }
            } 
            ListPool<Message>.Pool.Return(messages);
            //TOP
            for (int i = 0; i < Top_Lines; i++)
            {
                builder.AppendLine(i < tops.Count ? tops[i] : " ");
            }
            ListPool<string>.Pool.Return(tops);
            //CENTER TOP
            for (int i = 0; i < CenterTop_Lines; i++)
            {
                builder.AppendLine(i < center_tops.Count ? center_tops[i] : " ");
            }
            ListPool<string>.Pool.Return(center_tops);
            //CENTER
            int x = (int)Math.Round(((double)Center_Lines) / 2) - (int)Math.Floor(((double)centers.Count) / 2);
            for (int j = 1; j <= Center_Lines; j++)
            {
                if (j < x)
                {
                    builder.AppendLine(" ");
                    continue;
                }
                if (j - x < centers.Count)
                {
                    builder.AppendLine(centers[j - x]);
                    continue;
                }
                builder.AppendLine(" ");
            }
            ListPool<string>.Pool.Return(centers);
            //CENTER BOTTOM
            for (int i = 0; i < CenterBottom_Lines; i++)
            {
                builder.AppendLine(i < center_bottoms.Count ? center_bottoms[i] : " ");
            }
            ListPool<string>.Pool.Return(center_bottoms);
            //BOTTOM
            for (int a = 0; a < Bottom_Lines - bottoms.Count; a++)
            {
                builder.AppendLine(" ");
            }
            for (int c = bottoms.Count - 1; c >= 0; c--)
            {
                builder.AppendLine(bottoms[c]);
            }
            ListPool<string>.Pool.Return(bottoms);
            //SUMMARY
            string built_text = "<voffset=8.3em><size=70%><line-height=100%>" + builder.ToString();
            Player.SendHint(built_text, 2);
        }

        public void AddMsg(Message msg)
        {
            _messages.Add(msg);
        }

        public void DeleteMsg(string msgid) => _messages.RemoveAll(k => k.MsgId == msgid);

        public void DeleteMsg(Message msg) => _messages.Remove(msg);

        public void UpdateMsg(string msgid, string newcontent)
        {
            foreach (var a in _messages.Where(p => p.MsgId == msgid && p is PlainMessage))
                (a as PlainMessage).UpdateContent(newcontent);
        }

        //public static Thread UpdateThread;
        public static CoroutineHandle Coroutine;

        public static List<PlayerHud> Huds = new List<PlayerHud>();

        public static void SetEnabled()
        {
            //UpdateThread = new Thread(Update) { Name = "玩家HUD" };
            //UpdateThread.Start();
            Coroutine = Timing.RunCoroutine(Update());
            GameplayData.OnInitializingData += GameplayData_OnInitializingData;
            Exiled.Events.Handlers.Player.Destroying += Player_Destroying;
        }

        private static void GameplayData_OnInitializingData(Player player)
        {
            Huds.Add(new PlayerHud(player));
            player.SendRawMsg("profile", ProfileLine, 0, ScreenLocation.Bottom, false);
        }

        public static void Player_Destroying(DestroyingEventArgs ev)
        {
            Huds.RemoveAll(x => x.Player == ev.Player);
        }

        public static IEnumerator<float> Update()
        {
            while (true)
            {
                foreach (var x in Huds)
                {
                    if (x._lastUpdate.AddSeconds(1) < DateTime.Now)
                    {
                        try
                        {
                            x._messages.RemoveAll(k => k.Duration > 0 && k.Created.AddSeconds(k.Duration) < DateTime.Now);
                            x.Show();
                        }
                        catch (Exception e)
                        {
                            Log.Error(e);
                            continue;
                        }
                    }
                }
                yield return Timing.WaitForSeconds(0.2f);
            }
        }
    }

    public static class HudHandler
    {
        public static void SendRawMsg(this Player player, string msgid, Func<Player, string[]> content, uint duration, ScreenLocation location, bool update)
        {
            DynamicMessage msg = new DynamicMessage(msgid, player, content, duration, location);
            PlayerHud hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == player);
            if (hud != null)
            {
                hud.AddMsg(msg);
                if (update)
                {
                    hud.Show();
                }
            }
        }

        public static void SendRawMsg(this Player player, string msgid, string[] content, uint duration, ScreenLocation location, bool update)
        {
            PlainMessage msg = new PlainMessage(msgid, duration, location, content);
            PlayerHud hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == player);
            if (hud != null)
            {
                hud.AddMsg(msg);
                if (update)
                {
                    //hud.SetDirty();
                    hud.Show();
                }
            }
        }

        public static void SendRawMsg(this Player player, string msgid, string content, uint duration, ScreenLocation location, bool update)
        {
            PlainMessage msg = new PlainMessage(msgid, duration, location, content.Split('\n'));
            PlayerHud hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == player);
            if (hud != null)
            {
                hud.AddMsg(msg);
                if (update)
                {
                    //hud.SetDirty();
                    hud.Show();
                }
            }
        }

        public static void SendRawMsg(string msgid, Func<Player, string[]> content, uint duration, ScreenLocation location, bool update, params Player[] player)
        {
            player.ForEach(t => t.SendRawMsg(msgid, content, duration, location, update));
        }

        public static void SendRawMsg(string msgid, string[] content, uint duration, ScreenLocation location, bool update, params Player[] player)
        {
            player.ForEach(t => t.SendRawMsg(msgid, content, duration, location, update));
        }

        public static void SendRawMsg(string msgid, string content, uint duration, ScreenLocation location, bool update, params Player[] player)
        {
            player.ForEach(t => t.SendRawMsg(msgid, content, duration, location, update));
        }

        public static bool HasMsg(this Player player, string msgid)
        {
            var hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == player);
            return hud != null && hud._messages.Any(p => p.MsgId == msgid);
        }

        public static void SendFormattedMsg(this Player player, string msgid, string title, string content, uint duration, string icon, ScreenLocation location, bool update)
        {
            string text = $"『 {icon} |  {title}  |  {content} 』";
            player.SendRawMsg(msgid, text, duration, location, update);
        }

        public static void SendFormattedMsg(this Player player, string msgid, string title, string content, uint duration, MsgType type, ScreenLocation location, bool update)
        {
            string icon = string.Empty;
            switch (type)
            {
                case MsgType.Notification:
                    icon = "<color=#AAFFAA>🔔</color>"; break;
                case MsgType.Success_Response:
                    icon = "<color=#00FF00>✔</color>"; break;
                case MsgType.Failed_Response:
                    icon = "<color=#FF0000>❌</color>"; break;
                case MsgType.Message:
                    icon = "<color=#FFE4B5>✉</color>"; break;
                case MsgType.Announcement:
                    icon = "<color=#FF6A6A>📢</color>"; break;
            }
            string text = $"『 {icon} |  {title}  |  {content} 』";
            player.SendRawMsg(msgid, text, duration, location, update);
        }

        public static void SendFormattedMsg(string msgid, string title, string content, uint duration, MsgType type, ScreenLocation location, bool update, params Player[] player)
        {
            player.ForEach(t => t.SendFormattedMsg(msgid, title, content, duration, type, location, update));
        }

        public static void SendFormattedMsg(string msgid, string title, string content, uint duration, string icon, ScreenLocation location, bool update, params Player[] player)
        {
            player.ForEach(t => t.SendFormattedMsg(msgid, title, content, duration, icon, location, update));
        }

        public static void DeleteMsg(this Player player, string msgid, bool update)
        {
            PlayerHud hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == player);
            if (hud != null)
            {
                hud.DeleteMsg(msgid);
                if (update)
                {
                    //hud.SetDirty();
                    hud.Show();
                }
            }
        }

        public static void DeleteMsg(string msgid, bool update, params Player[] player)
        {
            foreach (Player p in player)
            {
                var hud = PlayerHud.Huds.FirstOrDefault(x => x.Player == p);
                hud.DeleteMsg(msgid);
                if (update)
                {
                    //hud.SetDirty();
                    hud.Show();
                }
            }
        }
    }

    public enum ScreenLocation
    {
        Top, CenterTop, Center, CenterBottom, Bottom
    }

    public enum MsgType
    {
        Announcement,
        Notification,
        Success_Response,
        Failed_Response,
        Message,
        Custom
    }
}